<template>
  <div class="z-input-number" :class="[
    { 'is-disabled': inputNumberDisabled },
    { 'is-controls-right': controlsAtRight },
    inputNumberSize ? 'z-input-number--' + inputNumberSize : '',
  ]">
    <span v-if="controls" class="z-input-number__decrease" :class="{ 'is-disabled': minDisabled }" role="button"
      @click="handleClick('decrease')">
      <i class="iconfont" :class="[controlsAtRight ? 'icon-arrow-down-bold' : 'icon-minus']"></i>
    </span>
    <input type="text" class="z-input__inner" :name="name" :value="inputVal" :disabled="inputNumberDisabled"
      @change="handleInputChange" @blur="handleBlur" @focus="handleFocus">
    <span v-if="controls" class="z-input-number__increase" :class="{ 'is-disabled': maxDisabled }" role="button"
      @click="handleClick('increase')">
      <i class="iconfont" :class="[controlsAtRight ? 'icon-arrow-up-bold' : 'icon-add']"></i>
    </span>
  </div>
</template>

<script>
export default {
  name: "zInputNumber"
};
</script>

<script setup>
import { ref, computed, getCurrentInstance } from 'vue';
const emit = defineEmits(['update:modelValue', 'change', 'focus', 'blur'])
const props = defineProps({
  modelValue: [Number, String],
  name: String,
  step: {
    type: Number,
    default: 1
  },
  max: {
    type: Number,
    default: Infinity
  },
  min: {
    type: Number,
    default: -Infinity
  },
  disabled: {
    type: Boolean,
    default: false
  },
  stepStrictly: {
    type: Boolean,
    default: false
  },
  precision: {
    type: Number,
    validator(val) {
      return val >= 0 && val === parseInt(val, 10);
    }
  },
  controls: {
    type: Boolean,
    default: true
  },
  controlsPosition: {
    type: String,
    default: ''
  },
  size: String
})
const instance = getCurrentInstance();

const inputVal = computed({
  get: () => props.modelValue,
  set: (newVal) => {
    let { min, max } = props
    let limit = [
      {
        validate: (value) => value <= min,
        res: min
      },
      {
        validate: (value) => value >= max,
        res: max
      },
      {
        validate: value => true,
        res: newVal * 1
      }
    ]
    let _val = limit.find(v => v.validate(newVal)).res
    if (props.precision) {
      emit('update:modelValue', _val.toFixed(props.precision))
      emit('change', _val.toFixed(props.precision))
    } else {
      emit('update:modelValue', _val)
      emit('change', _val)
    }
  }
})

const controlsAtRight = computed(() => {
  return props.controls && props.controlsPosition === 'right'
})

const inputNumberSize = computed(() => {
  return props.size
})

const minDisabled = computed(() => {
  return props.disabled || props.modelValue <= props.min
})

const maxDisabled = computed(() => {
  return props.disabled || props.modelValue >= props.max
})

const inputNumberDisabled = computed(() => {
  return props.disabled
})

const handleInputChange = (e) => {
  let value = e.target.value;
  const newVal = Number(value);
  if (!isNaN(newVal) || value === '') {
    setCurrentValue(newVal)
  } else {
    // 如果输入的非数字，则保留之前的数据
    e.target.value = inputVal.value
  }
}

const setCurrentValue = (val) => {
  const newVal = verifyValue(val)
  emit('update:modelValue', newVal)
  emit('change', newVal)
  // 针对只有第一次改值生效的问题
  instance.proxy.$forceUpdate();
}

const verifyValue = (value) => {
  const { min, max, step, precision, stepStrictly } = props
  let newVal = Number(value)
  if (newVal <= min) newVal = min
  if (newVal >= max) newVal = max
  if (stepStrictly) {
    newVal = Math.round(newVal / step) * step
  }
  return newVal
}

const handleClick = (type) => {
  if (props.disabled) return
  if (type === 'decrease') {
    inputVal.value = Number(inputVal.value) - props.step
  } else {
    inputVal.value = Number(inputVal.value) + props.step
  }
}

const handleBlur = (e) => {
  emit('blur', e)
}

const handleFocus = (e) => {
  emit('focus', e)
}

</script>

<style lang="scss" scoped>
@import '../../styles/components/input-number.scss';
</style>
